#include<iostream>
#include<string>
#include<set>
 
#include "Funcalyzer.h"
#include "Method.h"
#include "StringOps.h"
#include "Keywords.h"

using namespace std;

Funcalyzer::Funcalyzer(void)
{
	m_pxMethod = NULL;
    m_nDepthMax = 0;
	m_bInsideFunction = false;
    m_bReturnActive = false;
    m_bFuncCallActive = false;
	m_bFuncDefActive = false;
	
}

Funcalyzer::~Funcalyzer(void)
{
}

void Funcalyzer::onNewToken(Scope& inScope, Token& inToken)
{
	Scope::Type eScope = m_pxCurrentScope->getScopeType();
    char cDelimiter=char(inToken);

	//Funcalyzer gathers statement stats only inside functions
	if(!m_bInsideFunction)
        return;

    switch(cDelimiter)
    {
    case '\n':
		if(m_pxCurrentScope->isComment())
            m_pxMethod->m_xComplexity.m_nLoCom++;
        else
            m_pxMethod->m_xComplexity.m_nLoC++;
        break;
    
    case ';':
        if(!m_pxCurrentScope->isComment())
            m_pxMethod->m_xComplexity.m_nStatements++;
    }

	if(!m_pxCurrentScope->isCode())
		return;

	if(Keywords::isReturn(inToken.m_sValue))
	{
        m_bReturnActive = true;
		return;
	}

	if(m_bFuncDefActive)
	{
		char cDelimiter = char(inToken);
		if(cDelimiter == ';') 
			m_bFuncDefActive = false;
	}

	std::string sToken;
	if(!inToken.m_bIsDelimiter)
	{
		sToken = inToken.m_sValue;
		Symbol xSymbol;
		if(inScope.findSymbol(sToken, xSymbol))
		{
			if(xSymbol.m_eSymbolType == Symbol::eSymbolVariable)
			{
				m_pxMethod->m_asDataUse.push_back (sToken);
				m_pxMethod->m_anDataUsePoint.push_back(m_pxLocation->m_nLineNumber);

			}
		}
	}

	if (m_bReturnActive)
    {
		char cDelimiter = char(inToken);
		std::string sReturn="";
		std::string sReturnValue="";

		switch(cDelimiter)
		{
		case ';':
		case ',':
			sReturn = m_pxCurrentScope->m_asElement.back();
			copyFromFirstInstanceOf(sReturn, m_pxCurrentScope->m_sLastKeyword, sReturnValue);
			m_pxMethod->m_xReturn.m_asValues.push_back (sReturnValue);
			m_pxMethod->m_xReturn.m_anReturnPoint.push_back(m_pxLocation->m_nLineNumber);
			m_bReturnActive = false;
			break;
		}
	}
    
    if ( !((eScope==(Scope::eScopeBlock))||
         (eScope==Scope::eScopeParen)))
		 return;

	if(( false == inToken.m_bIsKeyword) &&
		(false == inToken.m_bIsDelimiter))
	{
		m_sFuncCall = inToken.m_sValue; //TODO: Add check for datatype. 
        m_bFuncCallActive = true;
		return;
	}

    if (m_bFuncCallActive)
	{
        if (string::npos != inToken.m_sValue.find_first_not_of("\t ("))
		{
            m_bFuncCallActive = false;
			m_sFuncCall = "";
		}
	}
}

void Funcalyzer::onScopeEntry(Scope& inNewScope, Scope& inOldScope)
{
    m_pxCurrentScope = &inNewScope;
    Scope::Type eNewScope = inNewScope.m_eType;

    switch(eNewScope)
    {
    case Scope::eScopeBlock:
		//FIXME: Hack, should be automatic from scuba
		inOldScope.m_asElement.push_back("");
		if(m_pxCurrentScope->isFunction())
			m_bInsideFunction = true;
        onEntryBlock(inOldScope);
        break;

    case Scope::eScopeParen:
        onEntryParen(inOldScope);
        break;

    case Scope::eScopeCommentC:
    case Scope::eScopeCommentCPP:
        onEntryComment(inOldScope);
        break;

    default:
        break;
    }
}

void Funcalyzer::onScopeExit(Scope& inNewScope, Scope& inOldScope)
{
    m_pxCurrentScope = &inNewScope;

    Scope::Type eNewScope		= inNewScope.m_eType;
	Scope::Type eOldScope		= inOldScope.m_eType;
	bool		bInNamespaceNow	= inNewScope.canDefineFunction();
	bool		bWasInFunction	= inOldScope.isFunction(); 

	if(bWasInFunction)
		m_bInsideFunction = false;

	if(bInNamespaceNow)
	{
		if(bWasInFunction)
		{
			m_apxMethod.push_back(m_pxMethod);
			m_pxMethod=NULL;
			m_nDepthMax = 0;
			return;
		}

		if(inOldScope.m_eType == Scope::eScopeParen)
		{
			gatherParameters(inOldScope);
			m_bFuncDefActive = true;
			return;
		}
	}

	if((eNewScope == Scope::eScopeBlock) &&
		(eOldScope == Scope::eScopeBlock))
	{
        m_nDepthMax--;
		return;
	}

}

void Funcalyzer::gatherParameters(Scope& inOldScope)
{
    std::string sArgument;
    std::string sArgumentName;
    std::string sArgumentType;
    Argument xArgument;
    size_t n = inOldScope.m_asElement.size();

    for(size_t i=0;i<n;i++)
    {
        sArgument = inOldScope.m_asElement[i];
		if(sArgument == "") //Substream will contain one extra ""
			continue;

        splitAsNameAndType(sArgument,sArgumentName,sArgumentType);

        xArgument.m_sName = trim(sArgumentName);
        xArgument.m_sTypePrefix = trim(sArgumentType);
        m_pxMethod->m_axArguments.push_back(xArgument);
    }
}

void Funcalyzer::onEntryParen(Scope& inOldScope)
{
    bool bWasInNamespace = inOldScope.canDefineFunction();

    if(bWasInNamespace)
    {
		std::string sMethod = inOldScope.m_asElement.back();
		std::string sMethodType;
		std::string sMethodName;

        m_pxMethod = new Method;
		m_pxMethod->m_nDefinePoint = m_pxLocation->m_nLineNumber;
        splitAsNameAndType(sMethod,sMethodName,sMethodType);
        m_pxMethod->m_sName = sMethodName;
        m_pxMethod->m_xReturn.m_sType = sMethodType;
    }

	if (m_bFuncCallActive)
    {
		m_pxMethod->m_asFunctionCall.push_back(m_sFuncCall);
		m_pxMethod->m_anFunctionCallPoint.push_back(m_pxLocation->m_nLineNumber);
		m_bFuncCallActive = false;
		m_sFuncCall = "";
    }
}

void Funcalyzer::onEntryBlock(Scope& inOldScope)
{
    Scope::Type eFromScope = inOldScope.m_eType;

    switch(eFromScope)
    {
    case Scope::eScopeBlock:
        if(!m_pxMethod)
            break;
        m_pxMethod->m_xComplexity.m_nPaths++;
        m_nDepthMax++;
        if(m_nDepthMax > m_pxMethod->m_xComplexity.m_nMaxDepth)
            m_pxMethod->m_xComplexity.m_nMaxDepth = m_nDepthMax;
        break;

    default:
        break;
    }
}

void Funcalyzer::onEntryComment(Scope& inOldScope)
{
    Scope::Type eFromScope = inOldScope.m_eType;

    switch(eFromScope)
    {
	case Scope::eScopeInvalid: //To remove warning
    default:
        if(m_pxMethod)
            m_pxMethod->m_xComplexity.m_nLoCom++;
        break;
    }
}


